#include "protocol_client.h"

#include "stream_client.h"
#include "weilapb.h"
#include "weilapb_defines.h"
#include "wlsdk_define.h"
#include "WLcJSON.h"

#ifdef __cplusplus
extern "C" {
#endif

#define PROTOCOL_CLIENT_TIMER_INTERVAL 1000  // 1000毫秒

typedef struct __protocol_client_request {
    wl_uint32                        seq;
    wl_uint32                        msgid;
    wl_uint32                        serviceid;
    wl_uint32                        commandid;
    wl_int32                         lifeleft;
    protocol_client_request_callback callback;
    void*                            context;
    wl_uint32                        contentlen;
    wl_char                          content[ZERO_CONTENT_SIZE];
} protocol_client_request;

typedef struct __protocol_client_listen {
    wl_uint32                       msgid;
    wl_uint16                       commandtype;
    protocol_client_listen_callback callback;
    void*                           context;
} protocol_client_listen;

typedef struct __protocol_client_global {
    wl_bool                        initialized;
    wl_list                        request_list;
    wl_list                        listen_list;
    WL_TIMER                       timer;
    protocol_client_session_depend session_depend;
    protocol_client_state_callback callback;
} protocol_client_global;

static protocol_client_global s_protocol_client_global;

void printPBEncResult(PBEncResult* pResult) {
    // printf("serviceId:%d\n", pResult->serviceId);
    // printf("commandId:%d\n", pResult->commandId);
    // printf("seq:%d\n", pResult->seq);
    // printf("errCode:%d\n", pResult->errCode);
    // printf("encodedDataSize:%d\n", pResult->encodedDataSize);
    WLLOGD("PB serviceid %d commandid %d seq %d errCode %d encodedDataSize %d",
           pResult->serviceId,
           pResult->commandId,
           pResult->seq,
           pResult->errCode,
           pResult->encodedDataSize);
}

void dumpData(unsigned char* pData, unsigned int dataSize) {
    // unsigned int i;
    // char         temp[1024];
    // char*        ptr = temp;
    // memset(temp, 0, 1024);
    // for (i = 0; i < dataSize; i++) {
    //     sprintf(ptr, "%2.2x", pData[i]);
    //     ptr += strlen(ptr);
    // }

    // printf("--------------\n%s\n---------------\n", temp);
}

static void request_error_callback(protocol_client_request* request, wl_int32 result_code) {
    if (wl_null != request->callback) {
        PBWeilaMsg message;
        memset(&message, 0, sizeof(message));
        message.message     = request->msgid;
        message.serviceId   = request->serviceid;
        message.commandId   = request->commandid;
        message.commandType = WEILA_MSG_RSP;
        message.resultCode  = result_code;
        request->callback(&message, request->content, request->contentlen, request->context);
    }
}

static void response_process(PBWeilaMsg* message) {
    wl_list_iterator         iterator;
    protocol_client_request* request =
        (protocol_client_request*)wl_list_get_first(&s_protocol_client_global.request_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // 请求匹配
        if (request->serviceid == message->serviceId && request->commandid == message->commandId
            && request->seq == message->seqNum) {
            WLLOGD("response callback %p serviceid %d commandid %d seq %d message serviceid %d commandid %d seq %d",
                   request->callback,
                   request->serviceid,
                   request->commandid,
                   request->seq,
                   message->serviceId,
                   message->commandId,
                   message->seqNum);
            if (request->callback) {
                request->callback(message, request->content, request->contentlen, request->context);
            }
            wl_free(request);
            wl_list_iterator_remove(&iterator);
            break;
        }
        request = (protocol_client_request*)wl_list_get_next(&iterator);
    }
}

static void listen_process(PBWeilaMsg* message) {
    wl_list_iterator        iterator;
    protocol_client_listen* listen =
        (protocol_client_listen*)wl_list_get_first(&s_protocol_client_global.listen_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        // 请求匹配
        if (listen->msgid == message->message && listen->commandtype == message->commandType) {
            if (listen->callback) {
                listen->callback(message, listen->context);
            }
            // break;
        }
        listen = (protocol_client_listen*)wl_list_get_next(&iterator);
    }
}

static void protocol_client_timer_callback(WL_TIMER timer, void* context) {
    wl_list_iterator         iterator;
    protocol_client_request* request = wl_null;
    if (timer != s_protocol_client_global.timer) {
        WLLOGW("timer %p is not equal local socket %p", timer, s_protocol_client_global.timer);
        wl_timer_stop(timer);
        return;
    }

    request = (protocol_client_request*)wl_list_get_first(&s_protocol_client_global.request_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        request->lifeleft -= PROTOCOL_CLIENT_TIMER_INTERVAL;
        // 请求超时
        if (0 >= request->lifeleft) {
            WLLOGW("remove timeout request callback %p serviceid %d commandid %d seq %d contentlen %d",
                   request->callback,
                   request->serviceid,
                   request->commandid,
                   request->seq,
                   request->contentlen);
            request_error_callback(request, WLSDK_RESULT_LOCAL_REQUEST_TIMEOUT);
            wl_free(request);
            request = (protocol_client_request*)wl_list_iterator_remove(&iterator);
        } else {
            request = (protocol_client_request*)wl_list_get_next(&iterator);
        }
    }

    // 没有请求,停止定时器
    if (0 >= wl_list_size(&s_protocol_client_global.request_list)) {
        if (WL_INVALID_TIMER != s_protocol_client_global.timer) {
            wl_timer_stop(s_protocol_client_global.timer);
            s_protocol_client_global.timer = WL_INVALID_TIMER;
        }
    }
}

static void reset_request(wl_int32 result_code) {
    wl_list_iterator         iterator;
    protocol_client_request* request =
        (protocol_client_request*)wl_list_get_first(&s_protocol_client_global.request_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        request_error_callback(request, result_code);
        request = (protocol_client_request*)wl_list_get_next(&iterator);
    }
    wl_list_free(&s_protocol_client_global.request_list);
    s_protocol_client_global.session_depend = PROTOCOL_CLIENT_SESSION_DEPEND_NONE;
}

static void protocol_stream_client_callback(wl_uint32 event, void* data, wl_uint32 datalen) {
    WLLOGT("event %d", event);
    switch (event) {
        case STREAM_CLIENT_EVENT_DISCONNECTED:
            if (s_protocol_client_global.callback) {
                s_protocol_client_global.callback(PROTOCOL_CLIENT_STATE_DISCONNECTED);
            }
            break;
        case STREAM_CLIENT_EVENT_CONNECTED:
            weilapb_reset_feedData();
            if (s_protocol_client_global.callback) {
                s_protocol_client_global.callback(PROTOCOL_CLIENT_STATE_CONNECTED);
            }
            break;
        case STREAM_CLIENT_EVENT_RECV_DATA:
            WLLOGD("weilapb_feedData datalen %d", datalen);
            weilapb_feedData(data, datalen);
            break;
        default:
            break;
    }
}

static void pb_event_listen(PBEvent* pEvent) {
    WLLOGD("event type %d", pEvent->eventType);
    switch (pEvent->eventType) {
        case PB_EVT_MSG_RSP:
            response_process((PBWeilaMsg*)(pEvent->pData));
            break;
        case PB_EVT_MSG_REQ:
        case PB_EVT_MSG_IND:
            listen_process((PBWeilaMsg*)(pEvent->pData));
            break;
        case PB_EVT_SESSION_MSG_IND:
            if (s_protocol_client_global.session_depend == PROTOCOL_CLIENT_SESSION_DEPEND_CONFIRM) {
                listen_process((PBWeilaMsg*)(pEvent->pData));
            } else {
                WLLOGW("protocol session depend %d not confirm", s_protocol_client_global.session_depend);
            }
            break;
        case PB_EVT_SYSTEM_ERR:
            protocol_client_disconnect();
            break;
        default:
            break;
    }

    if (pEvent->pData) {
        weilapb_releasePBWeilaMsg((PBWeilaMsg*)(pEvent->pData));
    }
}

wl_int32 protocol_client_init(protocol_client_state_callback callback) {
    if (!s_protocol_client_global.initialized) {
        PBFunHook hook;
        WLcJSON_Hooks jsonHook;

        hook.allocMem     = wl_mem_malloc;
        hook.freeMem      = wl_mem_free;
        hook.getTimeStamp = wl_utctime;
        hook.weilaLog     = wl_trace_weilapb;
        if (!weilapb_init(&hook)) {
            WLLOGW("weilapb_init fail");
            return -1;
        }

        jsonHook.malloc_fn = wl_mem_malloc;
        jsonHook.free_fn   = wl_mem_free;
        WLcJSON_InitHooks(&jsonHook);

        s_protocol_client_global.timer = WL_INVALID_TIMER;
        weilapb_registerListener(pb_event_listen);

        if (0 != wl_list_init(&s_protocol_client_global.request_list)) {
            WLLOGW("wl_list_init fail");
            return -1;
        }
        if (0 != wl_list_init(&s_protocol_client_global.listen_list)) {
            WLLOGW("wl_list_init fail");
            return -1;
        }
        if (0 != stream_client_init(protocol_stream_client_callback)) {
            WLLOGW("protocol_client_init fail");
            return -1;
        }
        s_protocol_client_global.session_depend = PROTOCOL_CLIENT_SESSION_DEPEND_NONE;
        s_protocol_client_global.callback       = callback;
        s_protocol_client_global.initialized    = wl_true;
    }
    WLLOGI("protocol_client_init");
    return 0;
}

wl_int32 protocol_client_end(void) {
    if (s_protocol_client_global.initialized) {
        reset_request(WLSDK_RESULT_LOCAL_SYSTEM_RELEASE);
        wl_list_free(&s_protocol_client_global.listen_list);
        if (WL_INVALID_TIMER != s_protocol_client_global.timer) {
            wl_timer_stop(s_protocol_client_global.timer);
            s_protocol_client_global.timer = WL_INVALID_TIMER;
        }
        stream_client_end();
        s_protocol_client_global.callback    = wl_null;
        s_protocol_client_global.initialized = wl_false;
    }
    WLLOGI("protocol_client_end");
    return 0;
}

wl_int32 protocol_client_get_state(void) {
    return stream_client_get_state();
}

void protocol_client_set_session_depend(protocol_client_session_depend depend) {
    s_protocol_client_global.session_depend |= depend;
}

wl_int32 protocol_client_connect_server(wl_char* server_addr, wl_uint16 server_port, wl_uint32 timeout) {
    return stream_client_connect(server_addr, server_port, timeout);
}

wl_int32 protocol_client_disconnect(void) {
    reset_request(WLSDK_RESULT_LOCAL_DISCONNECTED);
    return stream_client_disconnect();
}

wl_time protocol_client_get_latest_recv_timestamp(void) {
    return stream_client_get_latest_recv_timestamp();
}

wl_uint32 protocol_client_get_recv_bytes(void) {
    return stream_client_get_recv_bytes();
}

wl_int32 protocol_client_send_response(PBEncResult* encResult) {
    printPBEncResult(encResult);
    dumpData(encResult->pEncodedData, encResult->encodedDataSize);
    if (STREAM_CLIENT_STATE_CONNECTED != stream_client_get_state()) {
        WLLOGW("stream client disconnected");
        weilapb_releasePBEncResult(encResult);
        return WLSDK_RESULT_LOCAL_DISCONNECTED;
    }
    if (-1 == stream_client_write_data((wl_char*)encResult->pEncodedData, encResult->encodedDataSize)) {
        WLLOGW("stream_client_write_data fail");
        weilapb_releasePBEncResult(encResult);
        return WLSDK_RESULT_LOCAL_NETWORK_BLOCK;
    }
    weilapb_releasePBEncResult(encResult);
    return WLSDK_RESULT_SUCCESS;
}

wl_int32 protocol_client_clear_request(wl_uint32 msgid) {
    wl_list_iterator         iterator;
    protocol_client_request* request =
        (protocol_client_request*)wl_list_get_first(&s_protocol_client_global.request_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (msgid == request->msgid) {
            wl_free(request);
            request = (protocol_client_request*)wl_list_iterator_remove(&iterator);
        } else {
            request = (protocol_client_request*)wl_list_get_next(&iterator);
        }
    }
    return 0;
}

wl_int32 protocol_client_send_request(PBEncResult*                     encResult,
                                      void*                            content,
                                      wl_uint32                        contentlen,
                                      wl_uint32                        timeout,
                                      protocol_client_request_callback callback,
                                      void*                            context) {
    wl_int32 ret = WLSDK_RESULT_LOCAL_OUT_OF_MEMORY;
    printPBEncResult(encResult);
    dumpData(encResult->pEncodedData, encResult->encodedDataSize);
    if (STREAM_CLIENT_STATE_CONNECTED != stream_client_get_state()) {
        WLLOGW("stream client disconnected");
        weilapb_releasePBEncResult(encResult);
        return WLSDK_RESULT_LOCAL_DISCONNECTED;
    }
    if (-1 == stream_client_write_data((wl_char*)encResult->pEncodedData, encResult->encodedDataSize)) {
        WLLOGW("stream_client_write_data fail");
        weilapb_releasePBEncResult(encResult);
        return WLSDK_RESULT_LOCAL_NETWORK_BLOCK;
    } else {
        protocol_client_request* request = wl_malloc(sizeof(protocol_client_request) + contentlen);
        if (request) {
            request->seq        = encResult->seq;
            request->msgid      = encResult->message;
            request->serviceid  = encResult->serviceId;
            request->commandid  = encResult->commandId;
            request->lifeleft   = timeout;
            request->callback   = callback;
            request->context    = context;
            request->contentlen = contentlen;
            if (contentlen > 0) {
                memcpy(&request->content[0], content, contentlen);
            }
            WLLOGD("add request %p callback %p serviceid %d commandid %d seq %d contentlen %d",
                   request,
                   callback,
                   request->serviceid,
                   request->commandid,
                   request->seq,
                   request->contentlen);
            if (0 > wl_list_add(&s_protocol_client_global.request_list, request, -1)) {
                wl_free(request);
                WLLOGW("list add fail");
                ret = WLSDK_RESULT_LOCAL_OUT_OF_MEMORY;
            } else {
                ret = WLSDK_RESULT_SUCCESS;
                if (WL_INVALID_TIMER == s_protocol_client_global.timer) {
                    s_protocol_client_global.timer = wl_timer_start(PROTOCOL_CLIENT_TIMER_INTERVAL,
                                                                    REPETITIVE,
                                                                    protocol_client_timer_callback,
                                                                    wl_null,
                                                                    WL_TASK_APP,
                                                                    "protocol_client");
                }
            }
        }
    }
    weilapb_releasePBEncResult(encResult);
    return ret;
}

wl_int32 protocol_client_add_listen(wl_uint32                       msgid,
                                    wl_uint16                       commandtype,  // 参考COMMAND_TYPE
                                    protocol_client_listen_callback callback,
                                    void*                           context) {
    // wl_list_iterator        iterator;
    // protocol_client_listen* listen =
    //     (protocol_client_listen*)wl_list_get_first(&s_protocol_client_global.listen_list, &iterator);
    // while (!wl_list_iterator_is_end(&iterator)) {
    //     //同个消息只能监听一个
    //     if (listen->msgid == msgid && listen->commandtype == commandtype) {
    //         listen->callback = callback;
    //         listen->context  = context;
    //         return 0;
    //     }
    //     listen = (protocol_client_listen*)wl_list_get_next(&iterator);
    // }
    // 添加监听
    {
        protocol_client_listen* listen = wl_malloc(sizeof(protocol_client_listen));
        if (listen) {
            listen->msgid       = msgid;
            listen->commandtype = commandtype;
            listen->callback    = callback;
            listen->context     = context;
            if (0 > wl_list_add(&s_protocol_client_global.listen_list, listen, -1)) {
                wl_free(listen);
                WLLOGW("list add fail");
                return -1;
            }
            return 0;
        }
        return -1;
    }
}

#ifdef __cplusplus
}
#endif
